/**
 * 
 */
package com.retrozelda.nyanimals;

import java.util.Hashtable;

import com.retrozelda.nyanimals.INyanObject.Type;

import android.graphics.RectF;
import android.util.Log;
import android.view.MotionEvent;


/**
 * @author Erick
 *
 */
public class CFood extends CRectangle 
{
	enum FoodStates { NORMAL, SPOILED };
	enum FoodStatus 
	{ 
		FULL, EATEN_0, EATEN_1, EATEN_2, EMPTY;

		public FoodStatus Next() 
		{
			int myVal = this.ordinal();
			int nNextVal = myVal + 1;
			int nLastVal = FoodStatus.values().length - 1;
			FoodStatus[] all = FoodStatus.values();
			
			if(nNextVal > nLastVal)
			{
				return EMPTY;
			}
			
			return all[myVal + 1];
		}
	};

	Hashtable<FoodStates, Hashtable<FoodStatus, _AnimationTextureLink>> m_FoodAnimations;
	FoodStates m_CurState;
	FoodStatus m_CurStatus;

	CRectangle m_Shadow;
	
	// stats
	static float METER_MAX = 100.0f;
	float m_fSpoilMeter;
	float m_fBiteMeter;
	
	// rates
	float m_fSpoilRate;
	float m_fBiteAmmount;

	// others
	boolean m_bDragging;
	boolean m_bWasDragging;
	boolean m_bHeld;
	CNyanimal m_HeldBy;
	
	float GetSpoilMeter() { return m_fSpoilMeter;}
	float GetBiteMeter() { return m_fBiteMeter;}
	float GetSpoilRate() { return m_fSpoilRate;}
	float GetBiteAmmount() { return m_fBiteAmmount;}

	void SetSpoilMeter(float fAmt) { m_fSpoilMeter = fAmt;}
	void SetBiteMeter(float fAmt) { m_fBiteMeter = fAmt;}
	void SetSpoilRate(float fAmt) { m_fSpoilRate = fAmt;}
	void SetBiteAmmount(float fAmt) { m_fBiteAmmount = fAmt;}

	public boolean StoppedDragging() { return m_bDragging == false && m_bWasDragging == true;}
	public boolean IsDragging() { return m_bDragging;}
	public boolean IsHeld() { return m_bHeld;}
	
	void PickedUp(CNyanimal other)
	{
		m_HeldBy = other;
		// what happens when an object picks us up
		if(m_HeldBy != null)
		{
			m_bDragging = false;
			m_bWasDragging = false;
			m_bHeld = true;
			SetPriority(1);
		}
		// what happens when an object puts us down
		else
		{
			m_bHeld = false;
			SetPriority(0);
		}
	}
	
	public float TakeBite(float fBite)
	{
		m_fBiteMeter -= fBite;
		if(m_fBiteMeter <= 0.0f)
		{
			m_CurStatus = m_CurStatus.Next();
			m_fBiteMeter = METER_MAX;
			
			Log.d("CFood", "Food is now at bite: " + m_CurStatus.toString());
			
			// check if empty
			if(m_CurStatus == FoodStatus.EMPTY)
			{
				Log.d("CFood", "Food Empty!");
				SetObjectStatus(ObjStatus.DELETE);
				return -1.0f;
			}
		}
		
		return GetBiteAmmount();
	}
	
	public CFood()
	{
		m_FoodAnimations = new Hashtable<FoodStates, Hashtable<FoodStatus, _AnimationTextureLink>>();
		m_FoodAnimations.put(FoodStates.NORMAL, new Hashtable<FoodStatus, _AnimationTextureLink>());
		m_FoodAnimations.put(FoodStates.SPOILED, new Hashtable<FoodStatus, _AnimationTextureLink>());

		m_bDragging = false;
		m_bWasDragging = false;
		m_bHeld = false;
		m_HeldBy = null;
		
		m_fSpoilMeter = METER_MAX;
		m_fBiteMeter = METER_MAX;
		m_fBiteAmmount = 25.0f;
		m_fSpoilRate = 0.5f;
	}

	public Object clone()
	{
		CFood ret = (CFood)super.clone();
		InitAnimations(null);
		
		return (CFood)ret;
	}
	
	private void InitAnimations(NyanimalGLSurfaceView game)
	{
		int nFrameSize = 128;		
		
		_AnimationTextureLink normalLink = new _AnimationTextureLink();		
		_AnimationTextureLink normalLink_bite_1 = new _AnimationTextureLink();	
		_AnimationTextureLink normalLink_bite_2 = new _AnimationTextureLink();	
		_AnimationTextureLink normalLink_bite_3 = new _AnimationTextureLink();	
		normalLink.Create(game, "food_normal", R.drawable.meat, nFrameSize, nFrameSize, 1, 1, 0, true);
		normalLink_bite_1.Create(game, "food_normal_bite_1", R.drawable.meatbitten1x, nFrameSize, nFrameSize, 1, 1, 0, true);	
		normalLink_bite_2.Create(game, "food_normal_bite_2", R.drawable.meatbitten2x, nFrameSize, nFrameSize, 1, 1, 0, true);	
		normalLink_bite_3.Create(game, "food_normal_bite_3", R.drawable.meatbitten3x, nFrameSize, nFrameSize, 1, 1, 0, true);	
		m_FoodAnimations.get(FoodStates.NORMAL).put(FoodStatus.FULL, normalLink);		
		m_FoodAnimations.get(FoodStates.NORMAL).put(FoodStatus.EATEN_0, normalLink_bite_1);		
		m_FoodAnimations.get(FoodStates.NORMAL).put(FoodStatus.EATEN_1, normalLink_bite_2);		
		m_FoodAnimations.get(FoodStates.NORMAL).put(FoodStatus.EATEN_2, normalLink_bite_3);
		
		
		_AnimationTextureLink spoiledLink = new _AnimationTextureLink();	
		_AnimationTextureLink spoiledLink_bite_1 = new _AnimationTextureLink();	
		_AnimationTextureLink spoiledLink_bite_2 = new _AnimationTextureLink();	
		_AnimationTextureLink spoiledLink_bite_3 = new _AnimationTextureLink();		
		spoiledLink.Create(game, "food_spoiled", R.drawable.meatrotten, nFrameSize, nFrameSize, 1, 1, 0, true);
		spoiledLink_bite_1.Create(game, "food_spoiled_bite_1", R.drawable.meatbitten1xrot, nFrameSize, nFrameSize, 1, 1, 0, true);	
		spoiledLink_bite_2.Create(game, "food_spoiled_bite_2", R.drawable.meatbitten2xrot, nFrameSize, nFrameSize, 1, 1, 0, true);	
		spoiledLink_bite_3.Create(game, "food_spoiled_bite_3", R.drawable.meatbitten3xrot, nFrameSize, nFrameSize, 1, 1, 0, true);
		m_FoodAnimations.get(FoodStates.SPOILED).put(FoodStatus.FULL, spoiledLink);		
		m_FoodAnimations.get(FoodStates.SPOILED).put(FoodStatus.EATEN_0, spoiledLink_bite_1);		
		m_FoodAnimations.get(FoodStates.SPOILED).put(FoodStatus.EATEN_1, spoiledLink_bite_2);		
		m_FoodAnimations.get(FoodStates.SPOILED).put(FoodStatus.EATEN_2, spoiledLink_bite_3);
		
		
		m_CurState = FoodStates.NORMAL;
		m_CurStatus = FoodStatus.FULL;
		UpdateUV();
	}
	

	@Override
	public void Init(NyanimalGLSurfaceView game)
	{
		super.Init(game);
		InitAnimations(game);
		SetSize();
		
		// set the shadow
		m_Shadow = new CRectangle();
		m_Shadow.Init(game);
		CTexture shadowTexture = CTextureManager.GetInstance().LoadTexture(R.drawable.shadow, game);
		m_Shadow.SetTexture(shadowTexture);
	}
	


	@Override
	public void Update(float deltaTime)
	{
		super.Update(deltaTime);
		
		// only fall on top 4/5th of hte room
		int minX = 0;
		int maxX = m_nScreenWidth;
		int minY = 0;
		int maxY = m_nScreenHeight - (m_nScreenHeight / 5);
		int m_nShadowOffset = (int) m_scale[1] - 10;
		
		if(m_bHeld == true)
		{
			if(m_HeldBy != null)
			{
				int[] nPos = m_HeldBy.getPosition();
				SetPosition(nPos[0], nPos[1]);
			}
			else
			{
				Log.d("CFood", "Held but not by anything!");
			}
		}
		else
		{
				
			// falling stuff
			if(m_bDragging == false)
			{					
				
				float fFallRate = 75.0f;
	
				// check if the new position is valid	
				int[] projPos = getPosition();
				if((projPos[0] >= minX) && (projPos[1] >= minY) && (projPos[0] <= maxX) && (projPos[1] <= maxY))
				{
					// falling	
					//Log.d("CFood", "Falling!");
					int[] nPos = getPosition();
					int nNewY = nPos[1] + (int)(fFallRate);
					SetPosition(nPos[0], nNewY);
				}
				else
				{
					// not falling
					//Log.d("CFood", "Not Falling!");
				}
			}
			
			// update out spoiled amount
			if(m_CurState != FoodStates.SPOILED)
			{
				m_fSpoilMeter -= m_fSpoilRate * deltaTime;
				if(m_fSpoilMeter <= 0.0f)
				{
					Log.d("CFood", "Food is now Spoiled!");
					m_CurState = FoodStates.SPOILED;
				}
			}
		}
		
		// update our animations
		UpdateAnimationFrame(deltaTime);
		
		// update the shadow
		if(m_Shadow != null)
		{
			if(m_position[1] < maxY)
			{
				m_nShadowOffset = 20 + (int) (maxY - m_position[1] );
			}
			
			m_Shadow.SetPosition((int)m_position[0], ((int)m_position[1]) + m_nShadowOffset);
			m_Shadow.Update(deltaTime);
		}
	}

	@Override
	public void Draw(CArgumentGroup args)
	{
		if(m_Shadow != null)
		{
			m_Shadow.SetSize(64, 64);
			m_Shadow.Draw(args);
		}

		SetSize(64, 64);
		super.Draw(args);
	}

	@Override
	public void SetPosition(int posX, int posY)
	{
		super.SetPosition(posX, posY);
	}

	// in: touch position, touch state
	// touch state: 0 = button is pressed.  1 = pressed.  2 = held.  3 = released
	public boolean HandleTouch(int touchX, int touchY, int nTouchState)
	{

		m_bWasDragging = m_bDragging;
		if(m_BoundingRect != null)
		{
			boolean bTouchState = m_BoundingRect.contains((float)touchX, (float)touchY);
			switch(nTouchState)
			{
				case MotionEvent.ACTION_MOVE:
					if(m_bDragging == true)
					{
						SetPosition(touchX, touchY);
					}
					break;
				case MotionEvent.ACTION_UP:
					
					// stop dragging
					if(m_bDragging == true)
					{
						Log.d("CFood", "Drag Stop!");
						m_bDragging = false;
						SetPriority(0);
					}
					break;
				case MotionEvent.ACTION_DOWN:
					
					// start dragging if we touched the food
					if(bTouchState == true)
					{
						Log.d("CFood", "Drag Start!");
						m_bDragging = true;
						SetPriority(1);
					}
					else
					{
						// Log.d("CFood", "Drag Miss!");
					}
					break;
			}
			
		}
		else	
		{
			Log.d(CButton.class.getSimpleName(), "Invalid Food!");
		}
		return m_bDragging;
	}
	

	// TODO: put UpdateAnimationFrame and UpdateUV in an interface
	private void UpdateAnimationFrame(float deltaTime)
	{
		boolean newFrame = m_FoodAnimations.get(m_CurState).get(m_CurStatus).GetAnimation().Update(deltaTime);
		
		// check if we have new UVs
		if(newFrame == true)
		{
			UpdateUV();
		}
	}

	private void UpdateUV()
	{
		// get the UV rect
		m_Texture =  m_FoodAnimations.get(m_CurState).get(m_CurStatus).GetTexture();
		RectF frameRect = m_FoodAnimations.get(m_CurState).get(m_CurStatus).GetAnimation().GetFrameUV(m_Texture.GetWidth(), m_Texture.GetHeight(), false);

		// put the rect where it belongs in the vertices
		// top right
		m_TriangleVerticesData[3] = frameRect.right;
		m_TriangleVerticesData[4] = frameRect.top;
 
		// bottom right
		m_TriangleVerticesData[8] = frameRect.right;
		m_TriangleVerticesData[9] = frameRect.bottom;

		// top left
		m_TriangleVerticesData[13] = frameRect.left;
		m_TriangleVerticesData[14] = frameRect.top;

		// bottom left
		m_TriangleVerticesData[18] = frameRect.left;
		m_TriangleVerticesData[19] = frameRect.bottom;

		// save
		m_TriangleVertices.position(0);
		m_TriangleVertices.put(m_TriangleVerticesData).position(0);
	}

	@Override
	public void HandleCollision(INyanObject other, RectF overlap)
	{
		super.HandleCollision(other, overlap);
		
		if(other.GetType() == Type.FOOD)
		{
			CFood otherFood = (CFood)other;
			if(otherFood.IsDragging() == false && IsDragging() == false)
			{
				int[] pos = getPosition();
				int[] newPos = getPosition();
				
				// adjust for the X
				if(pos[0] >= overlap.centerX())
				{
					newPos[0] += (int)overlap.width();
				}
				else
				{
					newPos[0] -= (int)overlap.width();
				}
				
				// adjust for the Y
				if(pos[1] >= overlap.centerY())
				{
					newPos[1] += (int)overlap.height();
				}
				else
				{
					newPos[1] -= (int)overlap.height();
				}
				
				// smooth to the new pos
				int[] smoothPos = new int[2];
				int nDamping = 10;
				smoothPos[0] = pos[0] + ((newPos[0] - pos[0]) / nDamping);
				smoothPos[1] = pos[1] + ((newPos[1] - pos[1]) / nDamping);
				SetPosition(smoothPos[0], smoothPos[1]);
				//SetPosition(newPos[0], newPos[1]);
			}
		}
		
	}

	@Override
	public Type GetType() 
	{
		// TODO Auto-generated method stub
		return Type.FOOD;
	}
    
}
